package com.yilizhiyou.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yilizhiyou.entity.ScenicSpot;
import com.yilizhiyou.service.AiSearchService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

/**
 * AI搜索服务实现类
 * 集成DeepSeek API进行智能搜索
 * 
 * @author 伊犁智游开发团队
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
public class AiSearchServiceImpl implements AiSearchService {

    private static final Logger log = LoggerFactory.getLogger(AiSearchServiceImpl.class);
    private final RestTemplate restTemplate = new RestTemplate();
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Value("${yilizhiyou.ai.deepseek.api-key}")
    private String deepseekApiKey;

    @Value("${yilizhiyou.ai.deepseek.base-url}")
    private String deepseekBaseUrl;

    @Value("${yilizhiyou.ai.deepseek.model}")
    private String deepseekModel;

    @Value("${yilizhiyou.ai.deepseek.max-tokens}")
    private int maxTokens;

    @Value("${yilizhiyou.ai.deepseek.temperature}")
    private double temperature;

    @Override
    public Map<String, Object> aiSearch(String query, String context) {
        log.info("AI搜索请求: query={}, context={}", query, context);
        
        try {
            // 构建DeepSeek API请求
            String prompt = buildSearchPrompt(query, context);
            String response = callDeepSeekAPI(prompt);
            
            // 解析响应
            Map<String, Object> result = parseSearchResponse(response);
            result.put("query", query);
            result.put("timestamp", System.currentTimeMillis());
            
            return result;
        } catch (Exception e) {
            log.error("AI搜索失败", e);
            return createErrorResponse("AI搜索失败: " + e.getMessage());
        }
    }

    @Override
    public List<String> getSearchSuggestions(String partialQuery) {
        log.info("获取搜索建议: partialQuery={}", partialQuery);
        
        try {
            if (partialQuery == null || partialQuery.trim().length() < 2) {
                return getDefaultSuggestions();
            }
            
            String prompt = buildSuggestionPrompt(partialQuery);
            String response = callDeepSeekAPI(prompt);
            
            return parseSuggestionsResponse(response);
        } catch (Exception e) {
            log.error("获取搜索建议失败", e);
            return getDefaultSuggestions();
        }
    }

    @Override
    public List<ScenicSpot> intelligentSpotRecommendation(String query, List<ScenicSpot> allSpots) {
        log.info("智能景点推荐: query={}, spotsCount={}", query, allSpots.size());
        
        try {
            // 构建景点信息上下文
            String spotsContext = buildSpotsContext(allSpots);
            String prompt = buildRecommendationPrompt(query, spotsContext);
            String response = callDeepSeekAPI(prompt);
            
            // 解析推荐结果
            List<String> recommendedSpotNames = parseRecommendationResponse(response);
            
            // 根据推荐结果匹配景点
            return matchSpotsByName(recommendedSpotNames, allSpots);
        } catch (Exception e) {
            log.error("智能景点推荐失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<ScenicSpot> semanticSearch(String query, List<ScenicSpot> allSpots) {
        log.info("语义搜索: query={}, spotsCount={}", query, allSpots.size());
        
        try {
            // 构建语义搜索提示词
            String spotsContext = buildSpotsContext(allSpots);
            String prompt = buildSemanticSearchPrompt(query, spotsContext);
            String response = callDeepSeekAPI(prompt);
            
            // 解析语义搜索结果
            List<String> matchedSpotNames = parseSemanticSearchResponse(response);
            List<ScenicSpot> matchedByNames = matchSpotsByName(matchedSpotNames, allSpots);

            if (!matchedByNames.isEmpty()) {
                return matchedByNames;
            }

            // 尝试从自由文本中直接提取出现过的景点名称
            List<ScenicSpot> extracted = extractSpotsFromFreeText(response, allSpots);
            if (!extracted.isEmpty()) {
                return extracted;
            }

            // 最后回退：基于查询关键词进行本地匹配（如 草原、湖、河、桥 等）
            List<ScenicSpot> fallback = localKeywordFallback(query, allSpots);
            return fallback;
        } catch (Exception e) {
            log.error("语义搜索失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public String generateSearchSummary(String query, List<ScenicSpot> results) {
        log.info("生成搜索摘要: query={}, resultsCount={}", query, results.size());
        
        try {
            String resultsContext = buildResultsContext(results);
            String prompt = buildSummaryPrompt(query, resultsContext);
            String response = callDeepSeekAPI(prompt);
            
            return parseSummaryResponse(response);
        } catch (Exception e) {
            log.error("生成搜索摘要失败", e);
            return "搜索完成，找到 " + results.size() + " 个相关景点。";
        }
    }

    /**
     * 调用DeepSeek API
     */
    private String callDeepSeekAPI(String prompt) throws Exception {
        log.info("调用DeepSeek API: prompt长度={}", prompt.length());
        
        // 构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + deepseekApiKey);

        // 构建请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", deepseekModel);
        requestBody.put("messages", Arrays.asList(
            Map.of("role", "system", "content", "你是一个专业的伊犁旅游助手，擅长帮助用户搜索和推荐伊犁地区的景点、美食、活动等旅游相关信息。请用中文回答，回答要简洁明了。"),
            Map.of("role", "user", "content", prompt)
        ));
        requestBody.put("max_tokens", maxTokens);
        requestBody.put("temperature", temperature);
        requestBody.put("stream", false);

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);

        try {
            // 发送请求
            ResponseEntity<String> response = restTemplate.postForEntity(
                deepseekBaseUrl + "/chat/completions", 
                request, 
                String.class
            );

            if (response.getStatusCode() == HttpStatus.OK) {
                JsonNode jsonNode = objectMapper.readTree(response.getBody());
                if (jsonNode.has("choices") && jsonNode.get("choices").size() > 0) {
                    String content = jsonNode.get("choices").get(0).get("message").get("content").asText();
                    log.info("DeepSeek API调用成功，返回内容长度: {}", content.length());
                    return content;
                } else {
                    throw new RuntimeException("DeepSeek API返回格式异常");
                }
            } else {
                throw new RuntimeException("DeepSeek API调用失败: " + response.getStatusCode() + " - " + response.getBody());
            }
        } catch (Exception e) {
            log.error("DeepSeek API调用异常", e);
            throw new RuntimeException("DeepSeek API调用失败: " + e.getMessage());
        }
    }

    /**
     * 构建搜索提示词
     */
    private String buildSearchPrompt(String query, String context) {
        return String.format("""
            用户搜索查询: %s
            
            上下文信息: %s
            
            请根据用户的搜索查询，提供以下信息：
            1. 搜索意图分析
            2. 相关景点推荐
            3. 搜索建议
            4. 旅游贴士
            
            请以JSON格式返回结果，包含以下字段：
            - intent: 搜索意图
            - recommendations: 推荐景点列表
            - suggestions: 搜索建议
            - tips: 旅游贴士
            """, query, context != null ? context : "无");
    }

    /**
     * 构建建议提示词
     */
    private String buildSuggestionPrompt(String partialQuery) {
        return String.format("""
            用户输入的部分查询: %s
            
            请基于伊犁旅游相关内容，提供5个相关的搜索建议。
            建议应该包括：
            - 景点名称
            - 美食推荐
            - 活动类型
            - 旅游主题
            
            请以JSON数组格式返回建议列表。
            """, partialQuery);
    }

    /**
     * 构建推荐提示词
     */
    private String buildRecommendationPrompt(String query, String spotsContext) {
        return String.format("""
            用户查询: %s
            
            可用景点信息: %s
            
            请根据用户查询，从可用景点中选择最相关的3-5个景点进行推荐。
            考虑因素包括：
            - 景点类型匹配
            - 用户兴趣偏好
            - 景点特色
            - 地理位置
            
            请返回推荐的景点名称列表，以JSON数组格式。
            """, query, spotsContext);
    }

    /**
     * 构建语义搜索提示词
     */
    private String buildSemanticSearchPrompt(String query, String spotsContext) {
        return String.format("""
            用户自然语言查询: %s
            
            可用景点信息: %s
            
            请理解用户的自然语言查询意图，从可用景点中找到最匹配的景点。
            支持以下类型的查询：
            - 景点类型（如"自然风光"、"人文景观"）
            - 活动类型（如"拍照"、"徒步"、"文化体验"）
            - 地理位置（如"靠近市区"、"山区"）
            - 特色描述（如"适合家庭"、"浪漫"、"刺激"）
            
            请返回匹配的景点名称列表，以JSON数组格式。
            """, query, spotsContext);
    }

    /**
     * 构建摘要提示词
     */
    private String buildSummaryPrompt(String query, String resultsContext) {
        return String.format("""
            用户搜索查询: %s
            
            搜索结果: %s
            
            请为这次搜索生成一个简洁的摘要，包括：
            - 搜索主题
            - 找到的景点数量
            - 主要推荐
            - 简要说明
            
            摘要应该简洁明了，不超过100字。
            """, query, resultsContext);
    }

    /**
     * 构建景点上下文信息
     */
    private String buildSpotsContext(List<ScenicSpot> spots) {
        return spots.stream()
            .map(spot -> String.format("景点: %s, 类型: %s, 位置: %s, 描述: %s, 评分: %.1f", 
                spot.getName(), spot.getType(), spot.getLocation(), 
                spot.getDescription(), spot.getRating() != null ? spot.getRating() : 0.0))
            .collect(Collectors.joining("\n"));
    }

    /**
     * 构建搜索结果上下文
     */
    private String buildResultsContext(List<ScenicSpot> results) {
        return results.stream()
            .map(spot -> String.format("%s (%s) - %s", spot.getName(), spot.getType(), spot.getDescription()))
            .collect(Collectors.joining("\n"));
    }

    /**
     * 解析搜索响应
     */
    private Map<String, Object> parseSearchResponse(String response) {
        try {
            JsonNode jsonNode = objectMapper.readTree(response);
            Map<String, Object> result = new HashMap<>();
            result.put("intent", jsonNode.get("intent").asText());
            result.put("recommendations", jsonNode.get("recommendations"));
            result.put("suggestions", jsonNode.get("suggestions"));
            result.put("tips", jsonNode.get("tips").asText());
            return result;
        } catch (Exception e) {
            log.warn("解析搜索响应失败，使用原始响应", e);
            Map<String, Object> result = new HashMap<>();
            result.put("raw_response", response);
            return result;
        }
    }

    /**
     * 解析建议响应
     */
    private List<String> parseSuggestionsResponse(String response) {
        try {
            JsonNode jsonNode = objectMapper.readTree(response);
            List<String> suggestions = new ArrayList<>();
            for (JsonNode suggestion : jsonNode) {
                suggestions.add(suggestion.asText());
            }
            return suggestions;
        } catch (Exception e) {
            log.warn("解析建议响应失败，使用默认建议", e);
            return getDefaultSuggestions();
        }
    }

    /**
     * 解析推荐响应
     */
    private List<String> parseRecommendationResponse(String response) {
        try {
            JsonNode jsonNode = objectMapper.readTree(response);
            List<String> recommendations = new ArrayList<>();
            for (JsonNode rec : jsonNode) {
                recommendations.add(rec.asText());
            }
            return recommendations;
        } catch (Exception e) {
            log.warn("解析推荐响应失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 解析语义搜索响应
     */
    private List<String> parseSemanticSearchResponse(String response) {
        return parseRecommendationResponse(response); // 使用相同的解析逻辑
    }

    /**
     * 从LLM自由文本回复中提取出现过的景点名称并匹配
     */
    private List<ScenicSpot> extractSpotsFromFreeText(String response, List<ScenicSpot> allSpots) {
        if (response == null || response.isBlank()) {
            return new ArrayList<>();
        }

        String normalized = response.replaceAll("[\\s，。、“”\"!！?？:：;；()（）]", "");

        return allSpots.stream()
            .filter(spot -> {
                String name = spot.getName() == null ? "" : spot.getName();
                return !name.isBlank() && normalized.contains(name.replaceAll("[\\s]", ""));
            })
            .distinct()
            .collect(Collectors.toList());
    }

    /**
     * 基于常见中文关键词进行本地回退匹配，确保中文查询如“我想去草原”不会空结果
     */
    private List<ScenicSpot> localKeywordFallback(String query, List<ScenicSpot> allSpots) {
        if (query == null) return new ArrayList<>();
        String q = query.toLowerCase();

        // 关键词到谓词的简单映射
        boolean wantGrassland = q.contains("草原") || q.contains("grassland") || q.contains("那拉提") || q.contains("昭苏");
        boolean wantLake = q.contains("湖") || q.contains("lake") || q.contains("赛里木");
        boolean wantBridge = q.contains("桥") || q.contains("bridge") || q.contains("大桥");
        boolean wantCulture = q.contains("文化") || q.contains("人文") || q.contains("历史");

        List<ScenicSpot> candidates = allSpots;
        if (wantGrassland) {
            candidates = candidates.stream()
                .filter(s -> containsAny(s, "草原", "那拉提", "昭苏"))
                .collect(Collectors.toList());
        } else if (wantLake) {
            candidates = candidates.stream()
                .filter(s -> containsAny(s, "湖", "赛里木"))
                .collect(Collectors.toList());
        } else if (wantBridge) {
            candidates = candidates.stream()
                .filter(s -> containsAny(s, "桥", "大桥"))
                .collect(Collectors.toList());
        } else if (wantCulture) {
            candidates = candidates.stream()
                .filter(s -> containsAny(s, "人文", "文化", "博物馆", "口岸", "八卦城"))
                .collect(Collectors.toList());
        } else {
            // 通用回退：名称/描述/位置包含任意中文分词或英文token
            candidates = candidates.stream()
                .filter(s -> textContains(s.getName(), query) || textContains(s.getDescription(), query) || textContains(s.getLocation(), query))
                .collect(Collectors.toList());
        }

        return candidates;
    }

    private boolean containsAny(ScenicSpot s, String... keywords) {
        for (String k : keywords) {
            if (textContains(s.getName(), k) || textContains(s.getDescription(), k) || textContains(s.getLocation(), k)) {
                return true;
            }
        }
        return false;
    }

    private boolean textContains(String text, String keyword) {
        if (text == null || keyword == null) return false;
        return text.toLowerCase().contains(keyword.toLowerCase());
    }

    /**
     * 解析摘要响应
     */
    private String parseSummaryResponse(String response) {
        return response.trim();
    }

    /**
     * 根据名称匹配景点
     */
    private List<ScenicSpot> matchSpotsByName(List<String> spotNames, List<ScenicSpot> allSpots) {
        return spotNames.stream()
            .flatMap(name -> allSpots.stream()
                .filter(spot -> spot.getName().contains(name) || name.contains(spot.getName()))
                .findFirst()
                .stream())
            .distinct()
            .collect(Collectors.toList());
    }

    /**
     * 获取默认搜索建议
     */
    private List<String> getDefaultSuggestions() {
        return Arrays.asList(
            "那拉提草原",
            "赛里木湖",
            "伊犁河大桥",
            "哈萨克族文化",
            "伊犁美食"
        );
    }

    /**
     * 创建错误响应
     */
    private Map<String, Object> createErrorResponse(String error) {
        Map<String, Object> result = new HashMap<>();
        result.put("error", error);
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
}
